/*
 * PhotonRTOS础光实时操作系统 -- 精确计数器
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <photon/accurate_counter.h>
#include <photon/bug_build.h>

#include <asm/barrier.h>


intptr_t arch_accurate_add(intptr_t i, struct accurate_counter *v)
{
	uintptr_t tmp;
	intptr_t result;

	asm volatile("// arch_accurate_add\n"
		"1:	ldrex	%0, %2\n"
		"	add	%0, %0, %3\n"
		"	strex	%r1, %0, %2\n"
		"	teq	%r1, #0\n"
		"	bne	 1b\n"
			: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
			: "Ir" (i)
			: "memory");

	smp_mb();
	return result;
}

intptr_t arch_accurate_sub(intptr_t i, struct accurate_counter *v)
{
	uintptr_t tmp;
	intptr_t result;

	asm volatile("// arch_accurate_sub\n"
		"1:	ldrex	%0, %2\n"
		"	sub	%0, %0, %3\n"
		"	strex	%r1, %0, %2\n"
		"	teq	%r1, #0\n"
		"	bne	 1b\n"
			: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
			: "Ir" (i)
			: "memory");

	smp_mb();
	return result;
}

uintptr_t arch_accurate_xchg(uintptr_t x, volatile void *ptr, int32_t size)
{
	uintptr_t ret, tmp;

	switch (size) {
	case 1:
		asm volatile("//	__xchg1\n"
		"1:	ldrexb	%r0, %2\n"
		"	strexb	%r1, %r3, %2\n"
		"	teq	%r1, #0\n"
		"	bne	 1b\n"
			: "=&r" (ret), "=&r" (tmp), "+Q" (*(uint8_t *)ptr)
			: "r" (x)
			: "memory");
		break;
	case 2:
		asm volatile("//	__xchg2\n"
		"1:	ldrexh	%r0, %2\n"
		"	strexh	%r1, %r3, %2\n"
		"	teq %r1, #0\n"
		"	bne  1b\n"
			: "=&r" (ret), "=&r" (tmp), "+Q" (*(uint16_t *)ptr)
			: "r" (x)
			: "memory");
		break;
	case 4:
		asm volatile("//	__xchg4\n"
		"1:	ldrex	%r0, %2\n"
		"	strex	%r1, %r3, %2\n"
		"	teq %r1, #0\n"
		"	bne  1b\n"
			: "=&r" (ret), "=&r" (tmp), "+Q" (*(uint32_t *)ptr)
			: "r" (x)
			: "memory");
		break;
	case 8:
		BUG();
		break;
	default:
		BUG();
	}

	smp_mb();


	return ret;
}

intptr_t arch_accurate_cmpxchg(struct accurate_counter *ptr, intptr_t old, intptr_t new)
{
	intptr_t oldval;
	uintptr_t res;

	smp_mb();

	asm volatile("// arch_accurate_cmpxchg\n"
		"1:	ldrex	%1, %2\n"
		"	cmp	%1, %3\n"
		"	bne	2f\n"
		"	strex	%r0, %4, %2\n"
		"	teq %r0, #0\n"
		"	bne  1b\n"
		"2:"
			: "=&r" (res), "=&r" (oldval), "+Q" (ptr->counter)
			: "Ir" (old), "r" (new)
			: "cc");

	smp_mb();
	return oldval;
}
